﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using Pixysoft.Framework.Reflection.Core;

namespace Pixysoft.Framework.Reflection.Controller
{
    class DynamicMethodFactory
    {
        internal static DynamicMethodHandler CreateDynamicMethodInfoHandler(Type type, MethodInfo info)
        {
            int argIndex = 0;

            DynamicMethod dynamicMethod = new DynamicMethod("D" + Guid.NewGuid().ToString().Replace("-", "_"),
                MethodAttributes.Static | MethodAttributes.Public,
                CallingConventions.Standard, typeof(object), new Type[] { typeof(object), typeof(object[]) }, typeof(DynamicMethodFactory), true);

            ILGenerator generator = dynamicMethod.GetILGenerator();

            OpCodesFactory.LdArgument(generator, 0);
            OpCodesFactory.CastIfNeeded(generator, type);

            foreach (ParameterInfo parainfo in info.GetParameters())
            {
                OpCodesFactory.LdArgument(generator, 1);
                OpCodesFactory.LdArrayValue(generator, typeof(object), argIndex);
                OpCodesFactory.UnboxIfNeeded(generator, parainfo.ParameterType);
                OpCodesFactory.CastIfNeeded(generator, parainfo.ParameterType);
                argIndex++;
            }

            if (info.IsVirtual)
                generator.Emit(OpCodes.Callvirt, info);
            else
                generator.Emit(OpCodes.Call, info);

            if (info.ReturnType.Equals(typeof(void)))
                OpCodesFactory.LdNull(generator);

            OpCodesFactory.ReturnMethod(generator);
            return (DynamicMethodHandler)dynamicMethod.CreateDelegate(typeof(DynamicMethodHandler));
        }


        /// <summary>
        /// 动态创建attribute对象
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        internal static DynamicConstructorInfoHandler CreateDynamicAttributeHandler(CustomAttributeData attributeData)
        {
            int argIndex = 0;

            DynamicMethod dynamicMethod = new DynamicMethod("D" + Guid.NewGuid().ToString().Replace("-", "_"),
                MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard,
                typeof(object),
                new Type[] { typeof(object[]) },
                typeof(DynamicMethodFactory),
                true);
            ILGenerator generator = dynamicMethod.GetILGenerator();

            foreach (ParameterInfo parainfo in attributeData.Constructor.GetParameters())
            {
                OpCodesFactory.LdArgument(generator, 0);
                OpCodesFactory.LdArrayValue(generator, typeof(object), argIndex);
                OpCodesFactory.UnboxIfNeeded(generator, parainfo.ParameterType);
                argIndex++;
            }
            OpCodesFactory.CreateObject(generator, attributeData.Constructor);
            OpCodesFactory.ReturnMethod(generator);
            return (DynamicConstructorInfoHandler)dynamicMethod.CreateDelegate(typeof(DynamicConstructorInfoHandler));
        }

        /// <summary>
        /// 创建构造函数
        /// </summary>
        /// <param name="type"></param>
        /// <param name="constructorInfo"></param>
        /// <returns></returns>
        internal static DynamicConstructorInfoHandler CreateDynamicConstructorInfoHandler(Type type, ConstructorInfo constructorInfo)
        {
            int argIndex = 0;

            DynamicMethod dynamicMethod = new DynamicMethod("D" + Guid.NewGuid().ToString().Replace("-", "_"),
                MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(object), new Type[] { typeof(object[]) }, typeof(DynamicMethodFactory), true);
            ILGenerator generator = dynamicMethod.GetILGenerator();

            foreach (ParameterInfo parainfo in constructorInfo.GetParameters())
            {
                OpCodesFactory.LdArgument(generator, 0);
                OpCodesFactory.LdArrayValue(generator, typeof(object), argIndex);
                OpCodesFactory.UnboxIfNeeded(generator, parainfo.ParameterType);
                argIndex++;
            }
            OpCodesFactory.CreateObject(generator, constructorInfo);
            OpCodesFactory.ReturnMethod(generator);
            return (DynamicConstructorInfoHandler)dynamicMethod.CreateDelegate(typeof(DynamicConstructorInfoHandler));
        }


        /// <summary>
        /// 创建PropertyInfo的动态方法get
        /// </summary>
        /// <param name="type"></param>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        internal static DynamicPropertyGetHandler CreateGetHandler(Type type, PropertyInfo propertyInfo)
        {
            MethodInfo getMethodInfo = propertyInfo.GetGetMethod(true);
            int argIndex = 0;

            DynamicMethod dynamicGet = new DynamicMethod("D" + Guid.NewGuid().ToString().Replace("-", "_"), typeof(object), new Type[] { typeof(object), typeof(object[]) }, typeof(DynamicMethodFactory), true);
            ILGenerator generator = dynamicGet.GetILGenerator();

            OpCodesFactory.LdArgument(generator, 0);
            foreach (ParameterInfo parainfo in getMethodInfo.GetParameters())
            {
                OpCodesFactory.LdArgument(generator, 1);
                OpCodesFactory.LdIntOrEnum(generator, argIndex);
                generator.Emit(OpCodes.Ldelem_Ref);
                OpCodesFactory.UnboxIfNeeded(generator, parainfo.ParameterType);
                argIndex++;
            }
            generator.Emit(OpCodes.Callvirt, getMethodInfo);
            OpCodesFactory.BoxIfNeeded(generator, getMethodInfo.ReturnType);
            OpCodesFactory.ReturnMethod(generator);

            return (DynamicPropertyGetHandler)dynamicGet.CreateDelegate(typeof(DynamicPropertyGetHandler));
        }

        /// <summary>
        /// 创建PropertyInfo的动态方法get
        /// </summary>
        /// <param name="type"></param>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        internal static DynamicPropertySetHandler CreateSetHandler(Type type, PropertyInfo propertyInfo)
        {
            MethodInfo setMethodInfo = propertyInfo.GetSetMethod(true);
            int argCount = setMethodInfo.GetParameters().Length;
            int argIndex = 0;

            DynamicMethod dynamicSet = new DynamicMethod("D" + Guid.NewGuid().ToString().Replace("-", "_"), typeof(void), new Type[] { typeof(object), typeof(object), typeof(object[]) }, typeof(DynamicMethodFactory), true);
            ILGenerator generator = dynamicSet.GetILGenerator();

            OpCodesFactory.LdArgument(generator, 0);
            foreach (ParameterInfo parainfo in setMethodInfo.GetParameters())
            {
                if (argIndex + 1 >= argCount)
                    break;

                generator.Emit(OpCodes.Ldarg_2);
                OpCodesFactory.LdArrayValue(generator, typeof(object), argIndex);
                OpCodesFactory.UnboxIfNeeded(generator, parainfo.ParameterType);
                argIndex++;
            }
            OpCodesFactory.LdArgument(generator, 1);
            OpCodesFactory.UnboxIfNeeded(generator, setMethodInfo.GetParameters()[argIndex].ParameterType);
            generator.Emit(OpCodes.Call, setMethodInfo);
            OpCodesFactory.ReturnMethod(generator);

            return (DynamicPropertySetHandler)dynamicSet.CreateDelegate(typeof(DynamicPropertySetHandler));
        }



        /// <summary>
        /// 创建Field的动态方法get
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fieldInfo"></param>
        /// <returns></returns>
        internal static DynamicFieldGetHandler CreateGetHandler(Type type, FieldInfo fieldInfo)
        {
            DynamicMethod dynamicGet = CreateGetDynamicMethod(type);
            ILGenerator generator = dynamicGet.GetILGenerator();

            OpCodesFactory.LdArgument(generator, 0);
            generator.Emit(OpCodes.Ldfld, fieldInfo);
            OpCodesFactory.BoxIfNeeded(generator, fieldInfo.FieldType);
            OpCodesFactory.ReturnMethod(generator);

            return (DynamicFieldGetHandler)dynamicGet.CreateDelegate(typeof(DynamicFieldGetHandler));
        }

        /// <summary>
        /// 创建Field的动态方法set
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fieldInfo"></param>
        /// <returns></returns>
        internal static DynamicFieldSetHandler CreateSetHandler(Type type, FieldInfo fieldInfo)
        {
            DynamicMethod dynamicSet = CreateSetDynamicMethod(type);
            ILGenerator generator = dynamicSet.GetILGenerator();

            OpCodesFactory.LdArgument(generator, 0);
            OpCodesFactory.LdArgument(generator, 1);
            OpCodesFactory.UnboxIfNeeded(generator, fieldInfo.FieldType);
            generator.Emit(OpCodes.Stfld, fieldInfo);
            OpCodesFactory.ReturnMethod(generator);

            return (DynamicFieldSetHandler)dynamicSet.CreateDelegate(typeof(DynamicFieldSetHandler));
        }



        private static DynamicMethod CreateGetDynamicMethod(Type type)
        {
            return new DynamicMethod("D" + Guid.NewGuid().ToString().Replace("-", "_"), typeof(object), new Type[] { typeof(object) }, typeof(DynamicMethodFactory), true);
        }

        private static DynamicMethod CreateSetDynamicMethod(Type type)
        {
            return new DynamicMethod("D" + Guid.NewGuid().ToString().Replace("-", "_"), typeof(void), new Type[] { typeof(object), typeof(object) }, typeof(DynamicMethodFactory), true);
        }
    }
}

